#include <burst_kernel.h>
#define _ bus_debug_print

#if 0

#define COMP_DEFLATE_SEND
#define COMP_INFLATE_SEND

#define XIO_MAIN 0
#define XIO_HAND 1
#define XIO_HANDED 4
#define XIO_UDP 2
#define XIO_SYNC_READ 3
#define XIO_SYNC_WRITE 10
#define XIO_SYNC_WAITING 11
#define XIO_CONNECTING 5
#define XIO_ACCEPTING 6
#define XIO_WAITING 7
#define XIO_UDP_FROM 8

#define SOCKS5_VERSION 0x5
#define SOCKS5_CONNECT 0x1
#define SOCKS5_BIND 0x2
#define SOCKS5_UDP 0x3
#define SOCKS5_SUCCESS 0x0
#define SOCKS5_FAIL 0x3
#define SOCKS5_IPV4 0x1
#define SOCKS5_DOMAIN 0x3

#define FATAL "Fatal error: "
#define ERROR "Error: "
#define WARNING "Warning: "
#define MESSAGE "Message: "
#define DEBUGGING "DEBUG: "

static int AddrLen = sizeof(struct sockaddr);
extern struct rb_root tree;
int GlobalUsing = 0;

typedef struct socks5_request {
	u8 sr_ver;
	u8 sr_op;
	u8 sr_rsv;
	u8 sr_atype;
} S5REQUEST;


typedef struct socks5_reply {
	u8 sr_ver;
	u8 sr_result;
	u8 sr_rsv;
	u8 sr_atype;
} S5REPLY;

typedef struct socks5_udp {
	u8 sr_rsv1;
	u8 sr_rsv2;
	u8 sr_frag;
	u8 sr_atype;
} S5UDP;

void INLINE rate_cacl(size_t nbytes)
{
	return;
}

u32 socks5_get_my_ip(u16 adapter)
{
	u32 IP;
	struct hostent * hostp;
	char name[255];

	if(gethostname(name,255) == -1)
		return 0;
	hostp=gethostbyname(name);

	if(!hostp)
		return 0;

	memcpy(&IP,(u32*)hostp->h_addr_list[adapter],4);

	return IP;
}

u32 socks5_get_name_ip(u16 serial,u8 *name,char namelong)
{
	u32 IP;
	struct hostent * hostp;
	char buffer[65535];

	memcpy(buffer,name,namelong);

	hostp=gethostbyname((const char*)buffer);

	if(!hostp)
		return 0;

	memcpy(&IP,(u32*)hostp->h_addr_list[serial],4);

	return IP;
}

static void socks5_fill_sockaddr(u8 *buf, int len, struct sockaddr_in *out)
{
	S5REQUEST s5_request = {0};
	out->sin_family = AF_INET;
	memcpy(&s5_request, buf, sizeof(S5REQUEST));

	if(s5_request.sr_ver == 5) {

		switch(s5_request.sr_atype) {

		case SOCKS5_IPV4:
	
			memcpy(&out->sin_addr.S_un.S_addr, \
					&buf[sizeof(S5REQUEST)], \
					sizeof(u32));
	
			memcpy(&out->sin_port, &buf[sizeof(S5REQUEST) + sizeof(u32)], sizeof(u16));
	
			break;
		case SOCKS5_DOMAIN:
			out->sin_addr.S_un.S_addr = socks5_get_name_ip(0, \
										&buf[sizeof(S5REQUEST) + sizeof(u8)], \
										buf[sizeof(S5REQUEST)]);
			memcpy(&out->sin_port, \
					&buf[sizeof(S5REQUEST) + sizeof(u8) + buf[sizeof(S5REQUEST)]], \
					sizeof(u16));
			break;
		default:

			break;

		}

	}else if(s5_request.sr_ver == 4) {

		switch(1) {
		default:
			memcpy(&out->sin_addr.S_un.S_addr, \
					&buf[sizeof(u32)], \
					sizeof(u32));
	
			memcpy(&out->sin_port, &buf[sizeof(u16)], sizeof(u16));
			break;
		}

	}
	return;
}

static void socks5_get_my_sockaddr(int bind_sock, struct sockaddr_in *out)
{
	getsockname(bind_sock, (struct sockaddr *)&out, (int *)&AddrLen);

	return;
}

static int socks5_fill_buffer(u8 *buf, struct sockaddr_in *in)
{
	S5REPLY s5_reply = {0};
	s5_reply.sr_ver = SOCKS5_VERSION;
	s5_reply.sr_result = SOCKS5_SUCCESS;
	s5_reply.sr_rsv = SOCKS5_SUCCESS;
	s5_reply.sr_atype = SOCKS5_IPV4;

	*(u32 *)&buf[4] = in->sin_addr.s_addr;
	*(u16 *)&buf[8] = in->sin_port;

	memcpy(buf, &s5_reply, sizeof(S5REPLY));

	return sizeof(S5REPLY) + sizeof(u32) + sizeof(u16);
}

int socks5_calc_data_offset(u8 *buf)
{
	return sizeof(S5UDP) + buf[sizeof(S5UDP)] + sizeof(u8) + sizeof(u16);
}


u8 *socks5_get_data_offset(u8 *buf)
{
	return buf + sizeof(S5UDP) + buf[sizeof(S5UDP)] + sizeof(u8) + sizeof(u16);
}


/*
 * Temp. closed!
 

void socks5_udp_parse_send(void *rbtree, fd_set *fd_read, struct io_ctl *tmp_ioctl)
{
	u8 buf[MAX_BUF_SIZE / 2];
	int err;
	int len;
	struct sockaddr_in sa = {0};
	S5UDP s5_udp = {0};
	len = err = socks5_filter_top_recv(tmp_ioctl->ic_pair->ic_socket, buf, MAX_BUF_SIZE / 2, 0);
	if(err <= 0) {
		closesocket(tmp_ioctl->ic_socket);
		closesocket(tmp_ioctl->ic_pair->ic_socket);

		fd_tree_erase(rbtree, tmp_ioctl->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_pair->ic_socket);

		FD_CLR(tmp_ioctl->ic_socket, fd_read);
		FD_CLR(tmp_ioctl->ic_pair->ic_socket, fd_read);

		dlfree(tmp_ioctl->ic_pair);
		dlfree(tmp_ioctl);
		return;
	}
	socks5_fill_sockaddr(buf, 0, &sa);
	err = socks5_sendto_all(tmp_ioctl->ic_pair->ic_socket, \
							&sa, \
							socks5_get_data_offset(buf), \
							socks5_calc_data_offset(buf) - len);
	if(err <= 0) {

		closesocket(tmp_ioctl->ic_socket);
		closesocket(tmp_ioctl->ic_pair->ic_socket);

		fd_tree_erase(rbtree, tmp_ioctl->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_pair->ic_socket);

		FD_CLR(tmp_ioctl->ic_socket, fd_read);
		FD_CLR(tmp_ioctl->ic_pair->ic_socket, fd_read);

		dlfree(tmp_ioctl->ic_pair);
		dlfree(tmp_ioctl);

	}
	return;
}

void socks5_udp_parse_recv(void *rbtree, fd_set *fd_read, struct io_ctl *tmp_ioctl)
{
	u8 buf[MAX_BUF_SIZE / 2];
	int err;
	struct sockaddr_in sa = {0};
	S5UDP s5_udp = {0};
	socks5_fill_sockaddr(buf, 0, &sa);
	err = recvfrom(tmp_ioctl->ic_socket, 
			buf + 10, 
			MAX_BUF_SIZE / 2 - 10, 0, 
			(struct sockaddr *)&sa, 
			(int *)&AddrLen);
	if(err <= 0) {
		closesocket(tmp_ioctl->ic_socket);
		closesocket(tmp_ioctl->ic_pair->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_pair->ic_socket);
		FD_CLR(tmp_ioctl->ic_socket, fd_read);
		FD_CLR(tmp_ioctl->ic_pair->ic_socket, fd_read);
		dlfree(tmp_ioctl->ic_pair);
		dlfree(tmp_ioctl);
		return;
	}
	s5_udp.sr_atype = SOCKS5_IPV4;
	socks5_fill_buffer(buf, &sa);
	memcpy(buf, &s5_udp, sizeof(S5UDP));
	err = socks5_send_all(tmp_ioctl->ic_pair->ic_socket, buf, err + 10);
	if(err <= 0) {
		closesocket(tmp_ioctl->ic_socket);
		closesocket(tmp_ioctl->ic_pair->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_socket);
		fd_tree_erase(rbtree, tmp_ioctl->ic_pair->ic_socket);
		FD_CLR(tmp_ioctl->ic_socket, fd_read);
		FD_CLR(tmp_ioctl->ic_pair->ic_socket, fd_read);
		dlfree(tmp_ioctl->ic_pair);
		dlfree(tmp_ioctl);
	}
	return;
}

*/

void socks5_exception_handle(bus_event_t *ev)
{

}

void socks5_select_proc(bus_event_t *ev)
{
	u8 input_buf[MAX_BUF_SIZE];
	struct sockaddr_in sa = {0};
	S5REQUEST s5_request = {0};
	S5REPLY s5_reply = {SOCKS5_VERSION, SOCKS5_SUCCESS};
	struct io_ctl *ioctlp = 0;
	if(rw == SET_READ) {
		int err = 1;
		int tmp_fd_1 = fd;
		if(!tmp_ioctl) {
			goto out;
		}
		GlobalUsing = 1;
		switch(tmp_ioctl->ic_status) {
		case XIO_MAIN:
			ioctlp = (struct io_ctl *)dlzalloc(sizeof(struct io_ctl));
			ioctlp->ic_socket = accept(tmp_ioctl->ic_socket, (struct sockaddr *)&ioctlp->ic_sockaddr, (int *)&AddrLen);
			setnonblock(ioctlp->ic_socket , TRUE);
			socks5_setup_socket(ioctlp->ic_socket);
			ioctlp->ic_pair = 0;
			ioctlp->ic_status = XIO_HAND;
			module->add_fd(ioctlp->ic_socket, &ioctlp->ic_event_status, SET_READ);
			fd_tree_insert(&tree, ioctlp);
			break;
		case XIO_HAND:
			err = socks5_filter_top_recv(tmp_ioctl->ic_socket, (char *)input_buf, MAX_BUF_SIZE, 0);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			if(input_buf[0] == 4) {
				_(MESSAGE "Some one using SOCKS4 protocol.\n");
				goto socks4;
			}
			s5_reply.sr_ver = SOCKS5_VERSION;
			s5_reply.sr_result = SOCKS5_SUCCESS;
			err = socks5_send_all(tmp_ioctl->ic_socket, (u8 *)&s5_reply, 2);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			tmp_ioctl->ic_status = XIO_HANDED;
			break;
		case XIO_HANDED:
			err = socks5_filter_top_recv(tmp_ioctl->ic_socket, (char *)input_buf, MAX_BUF_SIZE, 0);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
socks4:
			memcpy(&s5_request, input_buf, sizeof(S5REQUEST));
			switch(s5_request.sr_op) {
			case SOCKS5_CONNECT:
				ioctlp = (struct io_ctl *)dlzalloc(sizeof(struct io_ctl));
				ioctlp->ic_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
				module->add_fd(ioctlp->ic_socket, &ioctlp->ic_event_status, SET_WRITE);
				setnonblock(ioctlp->ic_socket , TRUE);
				BUILD_PAIR(ioctlp, tmp_ioctl);
				ioctlp->ic_status = XIO_CONNECTING;
				tmp_ioctl->ic_status = XIO_WAITING;
				fd_tree_insert(&tree, ioctlp);
				socks5_fill_sockaddr(input_buf, err, &ioctlp->ic_sockaddr);
				connect(ioctlp->ic_socket, (struct sockaddr *)&ioctlp->ic_sockaddr, sizeof(struct sockaddr));
				break;
			case SOCKS5_BIND:
				ioctlp = (struct io_ctl *)dlzalloc(sizeof(struct io_ctl));
				ioctlp->ic_listen = ioctlp->ic_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
				module->add_fd(ioctlp->ic_socket, &ioctlp->ic_event_status, SET_READ);
				setnonblock(ioctlp->ic_socket , TRUE);
				BUILD_PAIR(ioctlp, tmp_ioctl);
				fd_tree_insert(&tree, ioctlp);
				socks5_fill_sockaddr(input_buf, err, &ioctlp->ic_sockaddr);
				ioctlp->ic_sockaddr.sin_addr.s_addr = INADDR_ANY;
				err = bind(ioctlp->ic_socket, (struct sockaddr *)&ioctlp->ic_sockaddr, AddrLen);
				if(err <= 0) {
					socks5_exception_handle(tmp_ioctl, 
								err, 
								module);
					break;
				}
				listen(ioctlp->ic_socket, 1);
				ioctlp->ic_status = XIO_ACCEPTING;
				tmp_ioctl->ic_status = XIO_WAITING;
				err = socks5_send_all(tmp_ioctl->ic_socket, input_buf, socks5_fill_buffer(input_buf, &ioctlp->ic_sockaddr));
				if(err <= 0) {
					socks5_exception_handle(tmp_ioctl, 
								err, 
								module);
				}
				break;
			case SOCKS5_UDP:
				ioctlp = (struct io_ctl *)dlzalloc(sizeof(struct io_ctl));
				ioctlp->ic_listen = ioctlp->ic_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
				module->add_fd(ioctlp->ic_socket, &ioctlp->ic_event_status, SET_READ);
				setnonblock(ioctlp->ic_socket , TRUE);
				BUILD_PAIR(ioctlp, tmp_ioctl);
				fd_tree_insert(&tree, ioctlp);
				socks5_fill_sockaddr(input_buf, err, &ioctlp->ic_sockaddr);
				ioctlp->ic_sockaddr.sin_addr.s_addr = INADDR_ANY;
				ioctlp->ic_status = XIO_UDP;
				tmp_ioctl->ic_status = XIO_UDP_FROM;
				err = socks5_send_all(tmp_ioctl->ic_socket, input_buf, socks5_fill_buffer(input_buf, &ioctlp->ic_sockaddr));
				if(err <= 0) {
					socks5_exception_handle(tmp_ioctl, 
								err, 
								module);
				}
				break;
			}
			break;
		case XIO_WAITING:
			err = socks5_filter_top_recv(tmp_ioctl->ic_socket, (char *)input_buf, MAX_BUF_SIZE, 0);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			break;
		case XIO_ACCEPTING:
			tmp_ioctl->ic_socket = accept(tmp_ioctl->ic_listen, (struct sockaddr *)&tmp_ioctl->ic_sockaddr, (int *)&AddrLen);
			setnonblock(tmp_ioctl->ic_socket , TRUE);
			socks5_setup_socket(tmp_ioctl->ic_socket);
			err = socks5_send_all(tmp_ioctl->ic_pair->ic_socket, input_buf, socks5_fill_buffer(input_buf, &tmp_ioctl->ic_sockaddr));
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			tmp_ioctl->ic_status = XIO_SYNC_READ;
			tmp_ioctl->ic_pair->ic_status = XIO_SYNC_READ;
			break;
		case XIO_SYNC_READ:
			err = socks5_filter_top_recv(tmp_ioctl->ic_socket, (char *)input_buf, MAX_BUF_SIZE, 0);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
#ifndef SOCKS5_USE_QUEUE
			err = socks5_send_all(tmp_ioctl->ic_pair->ic_socket, input_buf, err);
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
			}
#else
			socks5_add_sending_buffer(tmp_ioctl->ic_pair, (char *)input_buf, err, 0, module);
#endif
			break;
		case XIO_UDP_FROM:
			/* socks5_udp_parse_send(&tree, &fd_read, tmp_ioctl); */
			break;
		case XIO_UDP:
			/* socks5_udp_parse_recv(&tree, &fd_read, tmp_ioctl); */
			break;
		}
		GlobalUsing = 0;
	}
	if(rw == SET_WRITE) {
		int err = 1;
		int tmp_fd_1 = fd;
		struct io_ctl *tmp_ioctl = fd_tree_search(&tree, tmp_fd_1);
		if(!tmp_ioctl) {
			goto out;
		}
		GlobalUsing = 1;
		switch(tmp_ioctl->ic_status) {
		case XIO_CONNECTING:
			ioctlp = tmp_ioctl;
			setnonblock(ioctlp->ic_socket , TRUE);
			socks5_get_my_sockaddr(ioctlp->ic_socket, &ioctlp->ic_sockaddr);
			err = socks5_check_connect(ioctlp->ic_socket);
			if(err < 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			socks5_setup_socket(ioctlp->ic_socket);
			err = socks5_send_all(ioctlp->ic_pair->ic_socket, input_buf, socks5_fill_buffer(input_buf, &ioctlp->ic_sockaddr));
			if(err <= 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
				break;
			}
			module->add_fd(tmp_ioctl->ic_socket, &tmp_ioctl->ic_event_status, SET_READ);
			module->del_fd(tmp_ioctl->ic_socket, &tmp_ioctl->ic_event_status, SET_WRITE);
			ioctlp->ic_status = XIO_SYNC_READ;
			ioctlp->ic_pair->ic_status = XIO_SYNC_READ;
			break;
#ifdef SOCKS5_USE_QUEUE
		case XIO_SYNC_WRITE:
			err = socks5_process_sending_queue(tmp_ioctl, module);
			if(err < 0) {
				socks5_exception_handle(tmp_ioctl, 
							err, 
							module);
			}
			break;
#endif
		}
		GlobalUsing = 0;
	}
out:
	return 0;
}

int socks5_select_listen(struct event_module *module)
{
	struct io_ctl *ioctlp = (struct io_ctl *)dlzalloc(sizeof(struct io_ctl));
	struct sockaddr_in sa = {0};
	int listen_1 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	int err = 0;
	sa.sin_family = PF_INET;
	sa.sin_addr.s_addr = INADDR_ANY;
	sa.sin_port = htons(/* ArgList.al_port */ 1080 );
	err = bind(listen_1, (struct sockaddr *)&sa, AddrLen);
	if(err) {
		_(ERROR "Bind operation error.\n");
		getchar();
		exit(-1);
	}
	_(MESSAGE "Bind operation done.\n");
	err = listen(listen_1, 10);
	if(err) {
		_(ERROR "Listen socket operation error.\n");
		getchar();
		exit(-1);
	}
	_(MESSAGE "Listen socket operation done.\n");
	_(MESSAGE "Finite State Machine begin running.\n");
	ioctlp->ic_status = XIO_MAIN;
	ioctlp->ic_socket = listen_1;
	module->add_fd(listen_1, &ioctlp->ic_event_status, SET_READ);
	fd_tree_insert(&tree, ioctlp);
}

#endif